use std::{
    fs,
    path::{Path, PathBuf},
};

use anyhow::{Context, Result};
use chrono::{DateTime, Duration, Local, NaiveDateTime, Utc};
use regex::Regex;

use crate::domain::FileTimeScope;

/// Bin 文件扫描服务
/// 用于扫描指定目录下的 .bin 文件，并从文件名中提取时间信息
pub struct BinFileScanner;

impl BinFileScanner {
    /// 扫描指定目录下的所有 .bin 文件
    ///
    /// # 参数
    /// - `folder_path`: 要扫描的目录路径
    /// - `smprate`: 采样率 (Hz)
    /// - `channels`: 通道数
    /// - `depth`: 位深度 (bits)
    ///
    /// # 返回
    /// 返回按开始时间排序的 FileTimeScope 列表
    pub fn scan_bin_files(
        folder_path: impl AsRef<Path>,
        smprate: usize,
        channels: usize,
        depth: usize,
    ) -> Result<Vec<FileTimeScope>> {
        let folder_path = folder_path.as_ref();

        // 检查目录是否存在
        if !folder_path.exists() {
            return Ok(Vec::new());
        }

        // 收集所有 .bin 文件
        let bin_files = Self::collect_bin_files(folder_path)?;
        let mut scopes = Vec::with_capacity(bin_files.len());

        // 正则表达式: 匹配 "yyyy-MM-dd HH-mm-ss.bin" 格式
        let regex = Regex::new(r"^(\d{4})-(\d{2})-(\d{2}) (\d{2})-(\d{2})-(\d{2})\.bin$")
            .context("创建正则表达式失败")?;

        // 处理每个文件
        for file_path in bin_files {
            if let Some(scope) =
                Self::parse_bin_file(&file_path, &regex, smprate, channels, depth)?
            {
                scopes.push(scope);
            }
        }

        // 按开始时间排序
        scopes.sort_by(|a, b| a.start_time.partial_cmp(&b.start_time).unwrap());

        Ok(scopes)
    }

    /// 收集目录下的所有 .bin 文件
    fn collect_bin_files(folder_path: &Path) -> Result<Vec<PathBuf>> {
        let mut files = Vec::new();

        for entry in fs::read_dir(folder_path)
            .with_context(|| format!("遍历目录失败: {}", folder_path.display()))?
        {
            let entry = entry.context("读取目录项失败")?;
            let path = entry.path();

            // 只收集 .bin 文件
            if path.is_file() && path.extension().and_then(|e| e.to_str()) == Some("bin") {
                files.push(path);
            }
        }

        Ok(files)
    }

    /// 解析单个 bin 文件
    fn parse_bin_file(
        file_path: &Path,
        regex: &Regex,
        smprate: usize,
        channels: usize,
        depth: usize,
    ) -> Result<Option<FileTimeScope>> {
        // 获取文件名
        let file_name = match file_path.file_name().and_then(|s| s.to_str()) {
            Some(name) => name,
            None => return Ok(None),
        };

        // 使用正则表达式匹配文件名
        let caps = match regex.captures(file_name) {
            Some(caps) => caps,
            None => return Ok(None), // 不匹配则跳过
        };

        // 提取时间组件
        let year: i32 = caps.get(1).unwrap().as_str().parse().context("解析年份失败")?;
        let month: u32 = caps
            .get(2)
            .unwrap()
            .as_str()
            .parse()
            .context("解析月份失败")?;
        let day: u32 = caps.get(3).unwrap().as_str().parse().context("解析日期失败")?;
        let hour: u32 = caps
            .get(4)
            .unwrap()
            .as_str()
            .parse()
            .context("解析小时失败")?;
        let minute: u32 = caps
            .get(5)
            .unwrap()
            .as_str()
            .parse()
            .context("解析分钟失败")?;
        let second: u32 = caps
            .get(6)
            .unwrap()
            .as_str()
            .parse()
            .context("解析秒数失败")?;

        // 构造 NaiveDateTime
        let naive_dt = NaiveDateTime::parse_from_str(
            &format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", year, month, day, hour, minute, second),
            "%Y-%m-%d %H:%M:%S",
        )
        .context("解析时间失败")?;

        // 转换为 UTC DateTime
        let start_time: DateTime<Utc> = DateTime::from_naive_utc_and_offset(naive_dt, Utc);

        // 获取文件大小
        let file_size = fs::metadata(file_path)
            .with_context(|| format!("读取文件元数据失败: {}", file_path.display()))?
            .len();

        // 计算持续时间
        // duration = file_size / (smprate * channels * (depth / 8))
        let bytes_per_sample = depth as f64 / 8.0;
        let bytes_per_second = smprate as f64 * channels as f64 * bytes_per_sample;
        let duration_seconds = if bytes_per_second > 0.0 {
            file_size as f64 / bytes_per_second
        } else {
            0.0
        };

        // 计算结束时间
        let duration_ms = (duration_seconds * 1000.0).round() as i64;
        let end_time = start_time + Duration::milliseconds(duration_ms);

        // 转换为本地时区用于格式化显示
        let start_time_local = start_time.with_timezone(&Local);
        let end_time_local = end_time.with_timezone(&Local);

        Ok(Some(FileTimeScope {
            start_time: start_time.timestamp() as f64,
            start_time_format: start_time_local.format("%Y-%m-%d %H:%M:%S").to_string(),
            end_time: end_time.timestamp() as f64,
            end_time_format: end_time_local.format("%Y-%m-%d %H:%M:%S").to_string(),
            duration: duration_seconds,
            file_path: file_path.to_string_lossy().to_string(),
            is_empty: duration_seconds <= 0.0,
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_regex_match() {
        let regex = Regex::new(r"^(\d{4})-(\d{2})-(\d{2}) (\d{2})-(\d{2})-(\d{2})\.bin$").unwrap();

        // 测试合法的文件名
        assert!(regex.is_match("2024-01-01 12-00-00.bin"));
        assert!(regex.is_match("2023-12-31 23-59-59.bin"));

        // 测试不合法的文件名
        assert!(!regex.is_match("2024-01-01.bin"));
        assert!(!regex.is_match("2024-01-01 12:00:00.bin")); // 使用了冒号
        assert!(!regex.is_match("invalid.bin"));
    }
}
